﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: BaseFrameDecoder.cs
* 文件说明: 帧解码器基类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Net;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 帧解码器基类
    /// </summary>
    public abstract class BaseFrameDecoder : IDisposable
    {
        /// <summary>
        /// 释放
        /// </summary>
        public abstract void Dispose();

        /// <summary>
        /// 结束一次解码
        /// </summary>
        protected virtual void FinishDecode()
        {

        }

        /// <summary>
        /// 标签
        /// </summary>
        public object Tag { get; set; }

        /// <summary>
        /// GUID
        /// </summary>
        public Guid Guid { get; } = Guid.NewGuid();

        /// <summary>
        /// 解码器数据来源绑定的IP地址和端口
        /// </summary>
        public IPEndPoint IPEndPoint { get; set; }

        /// <summary>
        /// 帧数据除了数据外的长度
        /// </summary>
        protected int FrameExceptDataLength { get; set; } = 0;

        /// <summary>
        /// 解码函数
        /// </summary>
        protected abstract void Decoding();

        /// <summary>
        /// 获取或设置包数据的最小值（默认为0，不判断）
        /// </summary>
        public int MinFrameLength { get; set; } = 0;

        /// <summary>
        /// 获取或设置包数据的最大值（默认为0，不判断）
        /// </summary>
        protected int MaxFrameLength { get; set; } = 0;

        /// <summary>
        /// 复位
        /// </summary>
        public abstract void Reset();

        /// <summary>
        /// 缓存最大长度
        /// </summary>
        public int CacheMaxSize { get; set; } = ConstDefine.DefaultNetBufferSize;

        /// <summary>
        /// 上次完成解码时间
        /// </summary>
        public DateTime LastDecodedTime { get; protected set; }

        /// <summary>
        /// 上次解码数据时间
        /// </summary>
        public DateTime LastAddedTime { get; protected set; }

        /// <summary>
        /// 缓存超时，缓存时间为当前解码数据与上次解码成功时间差，默认800，单位ms，大于0判断
        /// </summary>
        public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromMilliseconds(800);

        /// <summary>
        /// 解码错误事件
        /// </summary>
        public event OnDecoderError OnDecoderError;

        /// <summary>
        /// 解码错误事件
        /// </summary>
        /// <param name="error">错误描述</param>
        /// <param name="data">数据</param>
        protected void DecoderError(string error, byte[] data)
        {
            OnDecoderError?.Invoke(this, new DecoderErrorEventArgs(error, data));
        }

        /// <summary>
        /// 解码错误事件
        /// </summary>
        /// <param name="error">错误描述</param>
        /// <param name="data">数据</param>
        protected void DecoderError(string error, string data)
        {
            OnDecoderError?.Invoke(this, new DecoderErrorEventArgs(error, data));
        }

        /// <summary>
        /// 每次解码后执行事件
        /// </summary>
        /// <param name="sender">对象</param>
        protected void DoAfterDecoder(object sender)
        {
            AfterDecoder?.Invoke(sender, EventArgs.Empty);
        }

        /// <summary>
        /// 每次解码后执行事件
        /// </summary>
        public event EventHandler AfterDecoder;

        /// <summary>
        /// 缓存超时大于0时，检查超时，判断当前解码数据与上次完成解码数据时间差，超时则复位
        /// </summary>
        protected void CheckTimeout()
        {
            LastAddedTime = DateTime.Now;
            if (LastDecodedTime.Year < 2000)
            {
                LastDecodedTime = LastAddedTime;
            }

            if (CacheTimeout > TimeSpan.Zero)
            {
                TimeSpan ts = LastAddedTime - LastDecodedTime;
                if (ts.TotalMilliseconds > CacheTimeout.TotalMilliseconds)
                {
                    LastDecodedTime = LastAddedTime;
                    Reset();
                }
            }
        }
    }
}
